/* 
 *  Copyright (C) 2007 Dip. Ing. dell'Informazione, University of Pisa, Italy
 *  http://info.iet.unipi.it/~cng/ns2voip/
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, USA
 */
/*
 */

#include <voip_decoder_optimal.h>
#include <stat.h>
#include <math.h>
#include <stdio.h>

static class VoipDecoderOptimalClass : public TclClass
{
        public:
                VoipDecoderOptimalClass() : TclClass ( "Application/VoipDecoderOptimal" ) {}
                TclObject* create ( int, const char*const* )
                {
                        return ( new VoipDecoderOptimal );
                }
}
class_voip_decoder_optimal;

VoipDecoderOptimal::VoipDecoderOptimal ()
{}

int
VoipDecoderOptimal::command ( int argc, const char*const* argv )
{
        return VoipDecoder::command ( argc, argv );
}

void
VoipDecoderOptimal::collectFrame ( VoipFrame& frame )
{
        // Access/add the current talkspurt data.
        TalkData& talk = talkData_[frame.talkspurt_];

        // If this is the first frame of this talkspurt, then update data.
        if ( talk.nframes_ == 0 )
        {
                talk.nframes_ = frame.nframes_;
                talk.delay_.resize ( talk.nframes_, -1.0 );
        }

        // Update the number of received frames.
        if ( talk.delay_[frame.frame_] < 0 ) ++talk.received_;

        // Add/update the delay sample for the current frame.
        double delay = NOW - frame.timestamp_;
        if ( talk.delay_[frame.frame_] < 0 || talk.delay_[frame.frame_] > delay )
        {
                talk.delay_[frame.frame_] = delay;
        }

        if ( talk.nframes_ == talk.received_ )
        {
                // If the last frame of this talkspurt has been added, then
                // compute the measures for this talkspurt.
                collectTalkspurt ( frame.talkspurt_ );

                // Remove this talkspurt.
                talkData_.erase ( frame.talkspurt_ );
        }
}

void
VoipDecoderOptimal::collectTalkspurt ( unsigned int nframes )
{
        // Access the current talkspurt data.
        TalkData& talk = talkData_[nframes];

        double bestMos  = -1.0;
        double bestRcvd = 0.0;
        double bestDelay = 0.0;
        double bestPer = 0.0;

        // Get the optimal value of the delay for this talkspurt.
        for ( unsigned int i = 0 ; i < talk.nframes_ ; i++ )
        {
                double per   = 0;
                double rcvd  = 0;
                double delay = talk.delay_[i];
                if ( delay < 0 ) continue;

                // Compute the packet loss with this delay.
                for ( unsigned int j = 0 ; j < talk.nframes_ ; j++ )
                {
                        if ( talk.delay_[j] < 0 || talk.delay_[j] > delay ) ++per;
                        else ++rcvd;
                }
                per /= talk.nframes_;

                double mos = eModel ( delay, per );

                if ( bestMos < 0 || mos > bestMos )
                {
                        bestMos = mos;
                        bestRcvd = rcvd;
                        bestDelay = delay;
                        bestPer = per;
                }
        }

        if ( bestMos > 0 )
        {
                Stat::put ( "voip_frame_sent", id_, talk.nframes_ );
                Stat::put ( "voip_frame_rcvd", id_, bestRcvd );
                Stat::put ( "voip_frame_delay", id_, bestDelay);
                Stat::put ( "voip_mos_talkspurt", id_, bestMos );
                Stat::put ( "voip_frame_per", id_, bestPer );

                double delay = 0;
                for ( unsigned int i = 0 ; i < talk.nframes_ ; i++ )
                {
                        if ( talk.delay_[i] < 0 ) continue;
                        delay += talk.delay_[i];
                }
                delay /= talk.received_;
                double per = 1 - ( ( double ) talk.received_ ) / talk.nframes_;
                double mos = eModel ( delay, per );
                Stat::put ( "voip_none_frame_sent", id_, talk.nframes_ );
                Stat::put ( "voip_none_frame_rcvd", id_, talk.received_ );
                Stat::put ( "voip_none_mos_talkspurt", id_, mos );
        }
        if ( bestMos >= mosThreshold_ ) {
                Stat::put("voip_%_of_bad_talkspurts", id_, 0.0);
        }
        else {
                Stat::put("voip_%_of_bad_talkspurts", id_, 1.0);
        }
}

void
VoipDecoderOptimal::collectEnd ()
{
        unsigned int max = 0;
        std::map<unsigned int, TalkData>::iterator it;
        for ( it = talkData_.begin () ; it != talkData_.end () ; ++it )
        {
                if ( it == talkData_.begin() || it->first > max )
                        max = it->first;
        }
        talkData_.erase ( max );
        for ( it = talkData_.begin () ; it != talkData_.end () ; ++it )
        {
                collectTalkspurt ( it->first );
        }
        // Collect Gloabl metrics
        // Call the base class collect End function.
        VoipDecoder::collectEnd();
}
